#ifndef __W_MATCHER_H_
#define __W_MATCHER_H_

#include "room.hpp"
#include <list>
#include <thread>
#include <condition_variable>

template<class T>
class match_queue
{
public:

    // 获取元素个数
    int size() { return _list.size();}
    // 判断队列是否为空
    bool empty() { return _list.empty(); }
    // 阻塞线程
    void wait() 
    {
        unique_lock<mutex> lock(_mutex);
        _cond.wait(lock); 
    }
    // 入队数据，并唤醒线程
    void push(const T& data)
    {
        unique_lock<mutex> lock(_mutex);
        _list.push_back(data);
        _cond.notify_all();
    }
    // 出队数据
    bool pop(T& data)
    {
        unique_lock<mutex> lock(_mutex);
        if(_list.empty())
            { return false;}
   
        data = _list.front();
        _list.pop_front();
        return true;
    }
    // 删除指定数据
    void remove(T& data)
    {
        unique_lock<mutex> lock(_mutex);
        _list.remove(data);
    }

private:
    list<T> _list;  //用list而不用queue，是因为有删除中间元素的需求
    mutex _mutex;   //互斥锁
    condition_variable _cond;   //该条件变量主要用于阻塞消费者，队列元素 < 2时，会被阻塞
};

class matcher
{
public:
    matcher(room_manager* rm, user_table* ut, online_manager* om)
        :_rm(rm), _ut(ut), _om(om),
        _th_rookie(&matcher::_th_rookie_entry,this),
        _th_expert(&matcher::_th_expert_entry,this),
        _th_guru(&matcher::_th_guru_entry,this)
    {
        DLOG("匹配管理模块初始化完成...");
    }

    bool add(uint64_t uid)
    {
        // 根据用户天梯分数，来判定进入哪个匹配队列
        Json::Value user;
        // 1. 通过用户id获取用户信息
        bool ret = _ut->select_by_id(uid, user);
        if(ret == false) 
        { 
            DLOG("获取用户%d 信息失败",uid);
            return false;
        }
        // 2. 添加到指定的队列
        int score = user["score"].asInt();
        if(score < 2000) 
            { _qe_rookie.push(uid); }
        else if(score >= 2000 && score < 3000) 
            { _qe_expert.push(uid); }
        else 
            {  _qe_guru.push(uid); }

        return true;
    }
    bool del(uint64_t uid)
    {
        // 根据用户天梯分数，来判定从哪个匹配队列中移除
        Json::Value user;
        // 1. 通过用户id获取用户信息
        bool ret = _ut->select_by_id(uid, user);
        if(ret == false) 
        { 
            DLOG("获取用户%d 信息失败",uid);
            return false;
        }
        // 2. 到指定的队列里移除
        int score = user["score"].asInt();
        if(score < 2000) 
            { _qe_rookie.remove(uid); }
        else if(score >= 2000 && score < 3000) 
            { _qe_expert.remove(uid); }
        else 
            {  _qe_guru.remove(uid); }

        return true;
    }

private:
    void handle_matche(match_queue<uint64_t>& mq)
    {
        while(true)
        {
            // 1. 队列中的玩家数量是否 >2,  <2时线程就阻塞
            while(mq.size() < 2)
            { mq.wait(); }
            // 2. 把两个玩家 拿出队列
            uint64_t uid1,uid2;
            if(mq.pop(uid1) == false)
            { continue; }
            if(mq.pop(uid2) == false)
            {
                this->add(uid1);
                continue;
            }
            // 3. 校验玩家是否都在线，如果有一个不在线，另一个要重新放回匹配队列中
            wsserver_t::connection_ptr conn1 = _om->get_conn_from_hall(uid1);
            if(conn1.get() == nullptr)
            {
                this->add(uid2);
                continue;
            }
            wsserver_t::connection_ptr conn2 = _om->get_conn_from_hall(uid2);
            if(conn2.get() == nullptr)
            {
                this->add(uid1);
                continue;
            }
            // 4. 为玩家创建游戏房间，将玩家加入房间
            room_ptr rp = _rm->create_room(uid1, uid2);
            if(rp.get() == nullptr)
            {
                this->add(uid1);
                this->add(uid2);
                continue;
            }
            // 5. 对玩家进行响应
            Json::Value resp;
            resp["optype"] = "match_success";
            resp["result"] = "true";
            string body;
            json_util::serialize(resp, body);
            conn1->send(body);
            conn2->send(body);
        }
        
    }
    void _th_rookie_entry() { return handle_matche(_qe_rookie);}
    void _th_expert_entry(){ return handle_matche(_qe_expert);}
    void _th_guru_entry(){ return handle_matche(_qe_guru);}
private:
    match_queue<uint64_t> _qe_rookie;   //菜鸟匹配队列
    match_queue<uint64_t> _qe_expert;   //高手匹配队列
    match_queue<uint64_t> _qe_guru;   //大师匹配队列
    thread _th_rookie;     //对应的匹配线程
    thread _th_expert;
    thread _th_guru;
    room_manager* _rm;  
    user_table* _ut;
    online_manager* _om;
};

#endif